有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何在Java中实现Scala apply方法

我想从Scala代码中调用一些Java代码。我想使用Scala的apply构造,所以我可以这样称呼它:

val led = OutPin(0)

而不是:

val led = new OutPin(0)

我天真地在Java代码中实现了另一个apply方法,如下所示:

public class OutPin {

    public OutPin(int pinNumber) {
    }

    public OutPin apply(int pinNumber) {
        return new OutPin(pinNumber);
    }
}

这不会使我的Scala代码(上面的第一行)编译,而是给我一个错误:

对象OutPin不是一个值

在Java中实现Scala的apply方法的正确方法是什么


共 (3) 个答案

  1. # 1 楼答案

    如果你想要一个在Scala中可用的apply方法,你应该在Scala端实现它,使用object将java类与实例化它的类同名

    object OutPin {
    
       def apply(pinNumber :Int) = new OutPin(pinNumber)
    
    }
    
  2. # 2 楼答案

    问题是Scala编译器说必须在同一个文件中定义两个伴生对象,否则会出现错误:

    Companions 'class OutPin' and 'object OutPin' must be defined in same file
    

    下面是另一种可能对你有用的方法。如果在一个单独的包中定义Scala OutPin,那么它就可以工作了。例如:

    Java类:

    package base;
    
    public class OutPin {
        private final int i;
    
        public OutPin(int i) {
            this.i = i;
        }
    
        public int getI() {
            return this.i;
        }
    }
    

    Scala类:

    package base.scala
    
    object OutPin {
      def apply(i: Int): base.OutPin = new base.OutPin(i)
    }
    

    Scala客户端示例:

    import base.scala.OutPin
    
    object Client extends App {
      val op = OutPin(1)
    
      println(op.getI)
    }
    

    运行客户端打印1

    要访问含糖的apply,您必须导入base.scala,而不仅仅是base。如果获得apply语法真的很重要,那么它可能是值得的

  3. # 3 楼答案

    您的问题不在于apply方法本身,而在于试图在Java中实现Scala单例对象

    我认为(但不确定)这是非常困难的,甚至是不可能的

    考虑一个非常非常简单的例子:

    object Obj;
    

    这将编译成两个JVM字节码文件Obj$.classObj.class。理论上,只需检查这两个类的字节码,并在Java中重新表达相同的内容,应该很容易。Scala singleton对象的基本结构非常简单:

    1. 对于单例对象Obj,必须生成Obj.classObj$.class
    2. Obj$类必须有一个public final static类型的Obj$字段,名为MODULE$,它将在类初始化时初始化,并引用singleton对象。在Scala中,对Obj.foo()的调用被映射到Obj$.MODULE$.foo()[…如果Obj有一个名为foo()的方法,也就是说!]
    3. Java编译器对这些Scala生成的类对一无所知,因此对于Java interop,Obj类包含静态函数,这些函数只转发给对Obj$.MODULE$上具有相同名称和签名的方法的调用

    这听起来很复杂,但其实没那么复杂。编写一对这样的Java类很简单。但是Scala编译器(2.10.3)仍然不会将这对代码识别为构成Scala单例。深入研究Scala编译器生成的单例字节码,您会发现有些细节很难用合法Java表达。[提示:javap -c -p -s -v <fully-qualified-class-name>]

    例如,最终的静态MODULE$字段由静态初始值设定项间接初始化。静态初始值设定项只构造一个Obj$对象,而不直接分配它。赋值发生在私有构造函数中。这在Java中是非法的:blank static final肯定必须在静态初始值设定项中初始化,并且不能在可能从初始值设定项外部多次调用的代码(如私有构造函数)中赋值。Scala编译器生成字节码,它尊重空的最终语义(因为只调用一次私有构造函数),但超过了java编译器验证这些语义的能力。因此,如果用Java表示,这段代码将被拒绝

    另外,Obj类(没有终端美元符号的版本)包含一个类型为ScalaSig的注释,它看起来非常复杂,很难手动复制(在Java或Scala中),至少对于我们这些不确定该注释是如何工作的人来说是这样

    在决定将一对类视为“值”之前,我不知道Scala编译器到底在寻找什么,这是一个有效的Scala单例对象,但Scala的设计人员选择了不让它变得简单,尽管基本方案很简单。他们可能希望保留重新组织scala单例对象如何转换为字节码的能力。让Java程序员合成scala单例对象将有效地使当前方案成为scala公共API的永久部分

    请注意,编写一个普通的非单例类,其实例具有apply(…)方法,因此可以像函数一样调用,这在Java中很容易,而且工作良好。下面是一只爪哇猫:

    public class Cat {
        public String apply( int i ) {
            return "Meow: " + i;
        }
    }
    

    以下是Scala的糖化apply的用法:

    Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> val Morris = new Cat;
    Morris: Cat = Cat@6b4feafa
    
    scala> Morris(8)
    res0: String = Meow: 8